> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/jaypopat/cf_ai_duet/llms.txt
> Use this file to discover all available pages before exploring further.

# Docker Deployment

> Deploy Duet using Docker and Docker Compose

Duet can be deployed using Docker for easy setup and portability. The Docker image includes all necessary dependencies for pair programming sessions.

## Dockerfile Overview

The Duet Dockerfile uses a multi-stage build:

1. **Build stage** - Compiles the Go binary
2. **Run stage** - Creates a minimal runtime environment with development tools

## What's Included

The Docker image includes:

* **Neovim** - Text editor for collaborative coding
* **Node.js & npm** - JavaScript runtime and package manager
* **OpenSSH** - SSH server and key management
* **Go binary** - Compiled Duet server

## Quick Start

<Steps>
  <Step title="Build the Docker image">
    ```bash theme={null}
    docker build -t duet .
    ```
  </Step>

  <Step title="Run the container">
    ```bash theme={null}
    docker run -p 2222:2222 duet
    ```
  </Step>

  <Step title="Connect via SSH">
    ```bash theme={null}
    ssh <username>@localhost -p 2222
    ```
  </Step>
</Steps>

## Configuration

### Environment Variables

The Docker container accepts configuration through command-line arguments:

```bash theme={null}
docker run -p 2222:2222 duet /app/duet \
  -addr :2222 \
  -hostkey /app/.ssh/id_ed25519 \
  -worker https://your-worker.workers.dev
```

### Port Mapping

The Dockerfile exposes port `2222` by default:

```dockerfile theme={null}
EXPOSE 2222
```

Map it to a different host port:

```bash theme={null}
docker run -p 22:2222 duet  # Maps host port 22 to container port 2222
```

### Volume Mounts

Persist workspaces across container restarts:

```bash theme={null}
docker run -p 2222:2222 \
  -v $(pwd)/workspaces:/app/workspaces \
  duet
```

<Note>
  Each room gets its own subdirectory under `/app/workspaces`.
</Note>

## Custom Worker URL

To use your own Cloudflare Worker:

```bash theme={null}
docker run -p 2222:2222 duet \
  /app/duet -addr :2222 \
  -hostkey /app/.ssh/id_ed25519 \
  -worker https://duet-cf-worker.<subdomain>.workers.dev
```

## Docker Compose

Create a `docker-compose.yml` file:

```yaml theme={null}
version: '3.8'

services:
  duet:
    build: .
    ports:
      - "2222:2222"
    volumes:
      - ./workspaces:/app/workspaces
    command: [
      "/app/duet",
      "-addr", ":2222",
      "-hostkey", "/app/.ssh/id_ed25519",
      "-worker", "https://your-worker.workers.dev"
    ]
    restart: unless-stopped
```

Run with:

```bash theme={null}
docker-compose up -d
```

## Production Deployment

### Fly.io

Duet includes a `fly.toml` configuration for deployment on Fly.io:

```toml theme={null}
app = 'duet-morning-meadow-5890'
primary_region = 'lhr'

[build]

[http_service]
  internal_port = 2222
  force_https = true
  auto_stop_machines = 'stop'
  auto_start_machines = true
  min_machines_running = 0
  processes = ['app']

[[services]]
  protocol = 'tcp'
  internal_port = 2222

  [[services.ports]]
    port = 22

[[vm]]
  memory = '1gb'
  cpu_kind = 'shared'
  cpus = 1
```

<Steps>
  <Step title="Install Fly CLI">
    ```bash theme={null}
    curl -L https://fly.io/install.sh | sh
    ```
  </Step>

  <Step title="Login to Fly.io">
    ```bash theme={null}
    fly auth login
    ```
  </Step>

  <Step title="Deploy the application">
    ```bash theme={null}
    fly deploy
    ```
  </Step>

  <Step title="Set secrets">
    If you need to configure the Worker URL via secrets:

    ```bash theme={null}
    fly secrets set WORKER_URL=https://your-worker.workers.dev
    ```
  </Step>
</Steps>

### Resource Configuration

The default Fly.io configuration uses:

* **Memory**: 1GB
* **CPU**: 1 shared CPU
* **Auto-scaling**: Machines stop when idle, start on demand
* **Minimum running**: 0 (cost-effective for low traffic)

<Note>
  The `auto_stop_machines` and `auto_start_machines` settings help reduce costs by stopping idle machines.
</Note>

## Security Considerations

### SSH Host Keys

The Dockerfile generates SSH host keys at build time:

```dockerfile theme={null}
RUN mkdir -p /app/.ssh && \
    ssh-keygen -t ed25519 -f /app/.ssh/id_ed25519 -N "" && \
    chown -R duet:duet /app/.ssh
```

<Warning>
  For production, consider mounting a persistent host key to avoid key changes on container restarts:

  ```bash theme={null}
  docker run -p 2222:2222 \
    -v $(pwd)/ssh-keys:/app/.ssh \
    duet
  ```
</Warning>

### Non-Root User

The container runs as a non-root user `duet` for security:

```dockerfile theme={null}
RUN adduser -D duet
USER duet
```

## Troubleshooting

### Check Container Logs

```bash theme={null}
docker logs <container-id>
```

### Interactive Shell

Access the container:

```bash theme={null}
docker exec -it <container-id> /bin/sh
```

### Verify SSH Key

Check if the host key exists:

```bash theme={null}
docker exec <container-id> ls -la /app/.ssh
```

### Common Issues

* **Connection refused**: Verify port mapping is correct
* **Permission denied**: Check SSH host key permissions
* **Worker not responding**: Verify Worker URL is accessible from container
